#include <dspSimulator/instruction/addad.h>


bool ADDAD::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    return checkStandardCondition(dspMachine);
}

void ADDAD::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                              std::shared_ptr<llvm::IRBuilder<>> irBuilder) {

}

std::shared_ptr<Instruction> ADDAD::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<ADDAD> addad;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<11>(data, 2, 0b11110010000) && existStandardCondition(data)) {
            addad = std::make_shared<ADDAD>(dspInstInfo->getAddress(), baseCycle);
            addad->creg_z = extractBits<4>(data, 28);
            addad->dst = extractBits<5>(data, 23).to_ulong();
            addad->src2 = extractBits<5>(data, 18).to_ulong();
            addad->src1 = extractBits<5>(data, 13).to_ulong();
            addad->s = data[1];
            addad->p = data[0];
            if (addad->s == 0) {
                addad->funcUnit = FuncUnit::D1;
            } else if (addad->s == 1) {
                addad->funcUnit = FuncUnit::D2;
            }
            addad->instFormat = InstFormat::Kind1;
        }
    }
    return addad;
}

void ADDAD::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
    if (instFormat == InstFormat::Kind1) {
        if (src2 >= 4 && src2 <= 7) {
            if (s == 0) {
                auto addrMode = dspMachine->getRegAAddressingMode(src2);
                if (addrMode == AddressingMode::Linear) {
                    *dstLoRegPtr = *src2LoRegPtr + (*src1LoRegPtr << 3);
                } else if (addrMode == AddressingMode::CircularBK0) {
                    auto block = dspMachine->getAMRBlockSize(0);
                    *dstLoRegPtr = *src2LoRegPtr + ((*src1LoRegPtr << 3) % block);
                } else if (addrMode == AddressingMode::CircularBK1) {
                    auto block = dspMachine->getAMRBlockSize(1);
                    *dstLoRegPtr = *src2LoRegPtr + ((*src1LoRegPtr << 3) % block);
                }
            } else {
                auto addrMode = dspMachine->getRegBAddressingMode(src2);
                if (addrMode == AddressingMode::Linear) {
                    *dstLoRegPtr = *src2LoRegPtr + (*src1LoRegPtr << 3);
                } else if (addrMode == AddressingMode::CircularBK0) {
                    auto block = dspMachine->getAMRBlockSize(0);
                    *dstLoRegPtr = *src2LoRegPtr + ((*src1LoRegPtr << 3) % block);
                } else if (addrMode == AddressingMode::CircularBK1) {
                    auto block = dspMachine->getAMRBlockSize(1);
                    *dstLoRegPtr = *src2LoRegPtr + ((*src1LoRegPtr << 3) % block);
                }
            }
        } else {
            *dstLoRegPtr = *src2LoRegPtr + (*src1LoRegPtr << 3);
        }
    }
}

std::string ADDAD::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    if (instFormat == InstFormat::Kind1) {
        res += getSrc2Reg32Name() + "," + getSrc1Reg32Name() + "," + getDstReg32Name();
    }
    return res;
}

std::shared_ptr<DspInstruction> ADDAD::clone() {
    return std::make_shared<ADDAD>(*this);
}
